package util.models;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;

public class LanguageModel {

	public Hashtable<String, Hashtable<String, Hashtable<String, Integer>>> trigrams = new Hashtable<String, Hashtable<String, Hashtable<String, Integer>>>();
	public Hashtable<String, Hashtable<String, Integer>> bigrams = new Hashtable<String, Hashtable<String, Integer>>();
	public Hashtable<String, Integer> unigrams = new Hashtable<String, Integer>();
	public Hashtable<String, Float> seeds = new Hashtable<String, Float>();

	public HashSet<String> candidates = new HashSet<String>();

	public Hashtable<String, Hashtable<String, String>> bigrams_type = new Hashtable<String, Hashtable<String, String>>();
	public Hashtable<String, String> unigrams_type = new Hashtable<String, String>();

	/**
	 * Global variables
	 * 
	 */

	public void addTrigram(String a, String b, String c, int freq_t) {
		Hashtable<String, Hashtable<String, Integer>> temporal_bi = null;

		if (trigrams.containsKey(a)) {

			temporal_bi = trigrams.get(a);

			if (temporal_bi.containsKey(b)) {
				Hashtable<String, Integer> temporal_uni = temporal_bi.get(b);

				temporal_uni.put(c, freq_t);
				temporal_bi.put(b, temporal_uni);
				trigrams.put(a, temporal_bi);

			} else {

				Hashtable<String, Integer> temporal_uni = new Hashtable<String, Integer>();
				temporal_uni.put(c, freq_t);
				temporal_bi = new Hashtable<String, Hashtable<String, Integer>>();
				temporal_bi.put(b, temporal_uni);
				trigrams.put(a, temporal_bi);
			}

		} else {
			temporal_bi = new Hashtable<String, Hashtable<String, Integer>>();
			Hashtable<String, Integer> temporal_uni = new Hashtable<String, Integer>();
			temporal_uni.put(c, freq_t);
			temporal_bi.put(b, temporal_uni);
			trigrams.put(a, temporal_bi);

		}

	}

	public void addBigram(String a, String b, int freq_b) {

		if (bigrams.containsKey(a)) {

			Hashtable<String, Integer> temp = bigrams.get(a);
			temp.put(b, freq_b);

			// additive

			bigrams.put(a, temp);
		} else {

			Hashtable<String, Integer> temp = new Hashtable<String, Integer>();
			temp.put(b, freq_b);
			bigrams.put(a, temp);

		}

	}

	public int getBigramFreq(String a, String b) {

		if (bigrams.containsKey(a)) {

			if (bigrams.get(a).containsKey(b)) {

				return bigrams.get(a).get(b);
			}
		}

		if (bigrams.containsKey(b)) {

			if (bigrams.get(b).containsKey(a)) {

				return bigrams.get(b).get(a);
			}
		}

		return 0;

	}

	public int getUnigramFreq(String a) {

		if (unigrams.containsKey(a)) {

			return unigrams.get(a);
		}
		return 0;

	}

	public int getTrigramFreq(String a, String b, String c) {

		if (trigrams.containsKey(a)) {

			if (trigrams.get(a).containsKey(b)) {
				if (trigrams.get(a).get(b).containsKey(c)) {

					return trigrams.get(a).get(b).get(c);
				}

			}

		}

		return 0;

	}

	public boolean bigramExists(String a, String b) {

		if (bigrams.containsKey(a)) {

			if (bigrams.get(a).containsKey(b)) {

				return true;
			}
		}

		if (bigrams.containsKey(b)) {

			if (bigrams.get(b).containsKey(a)) {

				return true;
			}
		}

		return false;
	}

	public boolean trigramExists(String a, String b, String c) {

		if (trigrams.containsKey(a)) {

			if (trigrams.get(a).containsKey(b)) {
				if (trigrams.get(a).get(b).containsKey(c)) {
					return true;

				}

			}
		}

		if (trigrams.containsKey(a)) {

			if (trigrams.get(a).containsKey(c)) {
				if (trigrams.get(a).get(c).containsKey(b)) {
					return true;

				}

			}
		}

		if (trigrams.containsKey(b)) {

			if (trigrams.get(b).containsKey(a)) {
				if (trigrams.get(b).get(a).containsKey(c)) {
					return true;

				}

			}
		}

		if (trigrams.containsKey(b)) {

			if (trigrams.get(b).containsKey(c)) {
				if (trigrams.get(b).get(c).containsKey(a)) {
					return true;

				}

			}
		}

		// -----------------------

		if (trigrams.containsKey(c)) {

			if (trigrams.get(c).containsKey(a)) {
				if (trigrams.get(c).get(a).containsKey(b)) {
					return true;

				}

			}
		}

		if (trigrams.containsKey(c)) {

			if (trigrams.get(c).containsKey(b)) {
				if (trigrams.get(c).get(b).containsKey(a)) {
					return true;

				}

			}
		}

		return false;
	}

	/**
	 * This also updates de list of candidates
	 * 
	 * WARNING: For mix models this is convenient
	 * 
	 * I do not know if it is also convenient for standard LM ranking
	 * 
	 * 
	 * @param a
	 * @param freq_u
	 */
	public void addUnigram(String a, int freq_u) {
		unigrams.put(a, freq_u);

	}

	public void mergeUnigrams(Hashtable<String, Integer> uni_b) {
		if (uni_b == null)
			return;

		Enumeration<String> keys = uni_b.keys();
		while (keys.hasMoreElements()) {

			String a = keys.nextElement();
			if (unigrams.containsKey(a)) {

				unigrams.put(a, unigrams.get(a) + uni_b.get(a));
			} else {

				unigrams.put(a, uni_b.get(a));
			}

		}

	}

	public void mergeBigrams(Hashtable<String, Hashtable<String, Integer>> bi_b) {

		if (bi_b == null)
			return;

		Enumeration<String> keys_a = bi_b.keys();

		while (keys_a.hasMoreElements()) {

			String a = keys_a.nextElement();

			Hashtable<String, Integer> uni_b = bi_b.get(a);
			Enumeration<String> keys_b = uni_b.keys();

			while (keys_b.hasMoreElements()) {

				String b = keys_b.nextElement();

				if (bigrams.containsKey(a)) {

					int total = bi_b.get(a).get(b);
					if (bigrams.get(a).containsKey(b)) {

						total = total + bigrams.get(a).get(b);

					}

					bigrams.get(a).put(b, total);
				} else {
					Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

					uni_temp.put(b, bi_b.get(a).get(b));

					bigrams.put(a, uni_temp);

				}

			}

		}

	}

	public void mergeTrigrams(
			Hashtable<String, Hashtable<String, Hashtable<String, Integer>>> tri_b) {

		Enumeration<String> keys_a = tri_b.keys();

		while (keys_a.hasMoreElements()) {
			String a = keys_a.nextElement();
			Enumeration<String> bi_keys = tri_b.get(a).keys();
			while (bi_keys.hasMoreElements()) {
				String b = bi_keys.nextElement();
				Enumeration<String> uni_keys = tri_b.get(a).get(b).keys();
				while (uni_keys.hasMoreElements()) {

					String c = uni_keys.nextElement();

					int total = tri_b.get(a).get(b).get(c);

					if (trigrams.containsKey(a)) {

						if (trigrams.get(a).containsKey(b)) {

							if (trigrams.get(a).get(b).containsKey(c)) {
								trigrams.get(a).get(b).put(c,trigrams.get(a).get(b).get(c) + total);

							} else { // no key c

								Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

								uni_temp.put(c, total);

								trigrams.get(a).get(b).put(c, total);

							}

						} else {// no key b

							Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();
							uni_temp.put(c, total);
							trigrams.get(a).put(b, uni_temp);

						}

					} else { // no a key

						Hashtable<String, Hashtable<String, Integer>> bi_temp = new Hashtable<String, Hashtable<String, Integer>>();
						Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

						uni_temp.put(c, total);
						bi_temp.put(b, uni_temp);

						trigrams.put(a, bi_temp);

					}

				}

			}

		}

	}

	public void addLanguageModel(LanguageModel lm_b) {

		mergeUnigrams(lm_b.unigrams);
		mergeBigrams(lm_b.bigrams);
		mergeTrigrams(lm_b.trigrams);

		// revised this, add candidate tags
		mergeCandidateTags(lm_b.candidates);

	}
	
	
	public void unionLanguageModel(LanguageModel lm_b) {

		unionUnigrams(lm_b.unigrams);
		unionBigrams(lm_b.bigrams);
		unionTrigrams(lm_b.trigrams);

		// revised this, add candidate tags
		unionCandidateTags(lm_b.candidates);

	}
	
	private void unionBigrams(
			Hashtable<String, Hashtable<String, Integer>> bi_b) {
		// TODO Auto-generated method stub
		
		
		if (bi_b == null)
			return;

		Enumeration<String> keys_a = bi_b.keys();

		while (keys_a.hasMoreElements()) {

			String a = keys_a.nextElement();

			Hashtable<String, Integer> uni_b = bi_b.get(a);
			Enumeration<String> keys_b = uni_b.keys();

			while (keys_b.hasMoreElements()) {

				String b = keys_b.nextElement();

				if (bigrams.containsKey(a)) {

					int total = bi_b.get(a).get(b);
					if (bigrams.get(a).containsKey(b)) {

					//	total = total + bigrams.get(a).get(b);

					}

					bigrams.get(a).put(b, total);
				} else {
					Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

					uni_temp.put(b, bi_b.get(a).get(b));

					bigrams.put(a, uni_temp);

				}

			}

		}
		
	}

	private void unionCandidateTags(HashSet<String> candidates2) {
		// TODO Auto-generated method stub
		
		
		Iterator<String> candidate = candidates2.iterator();
		while (candidate.hasNext()) {

			candidates.add(candidate.next());

		}
		
	}

	private void unionTrigrams(
			Hashtable<String, Hashtable<String, Hashtable<String, Integer>>> tri_b) {
		// TODO Auto-generated method stub
		

		Enumeration<String> keys_a = tri_b.keys();

		while (keys_a.hasMoreElements()) {
			String a = keys_a.nextElement();
			Enumeration<String> bi_keys = tri_b.get(a).keys();
			while (bi_keys.hasMoreElements()) {
				String b = bi_keys.nextElement();
				Enumeration<String> uni_keys = tri_b.get(a).get(b).keys();
				while (uni_keys.hasMoreElements()) {

					String c = uni_keys.nextElement();

					int total = tri_b.get(a).get(b).get(c);

					if (trigrams.containsKey(a)) {

						if (trigrams.get(a).containsKey(b)) {

							if (trigrams.get(a).get(b).containsKey(c)) {
							//	trigrams.get(a).get(b).put(c, trigrams.get(a).get(b).get(c) + total);

							} else { // no key c

								Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

								uni_temp.put(c, total);

								trigrams.get(a).get(b).put(c, total);

							}

						} else {// no key b

							Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();
							uni_temp.put(c, total);
							trigrams.get(a).put(b, uni_temp);

						}

					} else { // no a key

						Hashtable<String, Hashtable<String, Integer>> bi_temp = new Hashtable<String, Hashtable<String, Integer>>();
						Hashtable<String, Integer> uni_temp = new Hashtable<String, Integer>();

						uni_temp.put(c, total);
						bi_temp.put(b, uni_temp);

						trigrams.put(a, bi_temp);

					}

				}

			}

		}

		
		
		
	}

	private void unionUnigrams(Hashtable<String, Integer> uni_b) {
		// TODO Auto-generated method stub
		if (uni_b == null)
			return;

		Enumeration<String> keys = uni_b.keys();
		while (keys.hasMoreElements()) {

			String a = keys.nextElement();
			if (unigrams.containsKey(a)) {

		
			} else {

				unigrams.put(a, uni_b.get(a));
			}

		}
	}

	public void aggregateLanguageModel(LanguageModel lm_b) {

		mergeUnigrams(lm_b.unigrams);
		mergeBigrams(lm_b.bigrams);
		mergeTrigrams(lm_b.trigrams);

		// revised this, add candidate tags
		mergeCandidateTags(lm_b.candidates);

	}

	private void mergeCandidateTags(HashSet<String> candidates2) {
		// TODO Auto-generated method stub

		Iterator<String> candidate = candidates2.iterator();
		while (candidate.hasNext()) {

			candidates.add(candidate.next());

		}

	}

}
